package com.wkk.vo;

import com.wkk.annotation.ResponseResultBody;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.annotation.Annotation;

/**
 * @author : WangKeKe
 * @date : 2020/9/20 14:22
 * Description : 实现ResponseResultBody注解效果
 * <p>
 * - ResponseBodyAdvice接口是在Controller执行return之后，
 * - 在response返回给浏览器或者APP客户端之前，
 * - 执行的对response的一些处理。可以实现对response数据的一些统一封装或者加密等操作。
 * <p>
 * - 通过supports方法，我们可以选择哪些类，或者哪些方法要对response进行处理，其余的则不处理。
 * <p>
 * - beforeBodyWrite方法中，可对response处理的具体代码。
 */

@Slf4j
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {
    /**
     * 获取ResponseResultBody注解类
     */
    private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;


    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {

        // 该方法可以判断类或者方法是否使用了 @ResponseResultBody
        return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE) || returnType.hasMethodAnnotation(ANNOTATION_TYPE);
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {

        // - 我们可以通过修改body中的数据对其进行加密等等操作

        // - 防止重复包裹的问题出现
        // - 以及实现统一的返回格式 但是状态码只能唯一
        // - 我们也可以通过
        return body instanceof Result ? body : Result.ok(body);
    }
}
